package gti310.tp4;

import java.util.*;

/**
 * La DCT (Discrete Cosine Transform) est une transformation proche de 
 * la transformée de Fourier discrète (DFT). Cette classe applique le concept
 * avec la méthode applyDCT et effectue l'inverse avec la méthode dctInverse. 
 * 
 * @author Émile Robinson et Fredy Alexander Bonilla
 * @date 2014-12-04
 */
public class dct {

	//Variables 
	private static final int N = 8;
	private double[] c = new double[N];
	private ListeComposant liste;

	/**
	 * Constructeur de la classe dct
	 */
	public dct(ListeComposant liste) {
		this.initializeCoefficients();
		this.liste = liste;
	}

	/**
	 * Méthode appellé à l'interne du constructeur qui sert à initialiser les 
	 * cofficients
	 */
	private void initializeCoefficients() {
		for (int i=1;i<N;i++) {
			c[i]=1;
		}
		c[0]=1/Math.sqrt(2.0);
	}

	/**
	 * La méthode parcourt la liste de component et appel la méthode prevue applyDCT sur
	 * chacun des bloc 8x8. Par la suite le bloc est mise à jour dans la liste. 
	 */
	public void executerDCT(){
		
		double [][] F = new double[N][N];

		//Application de la DCT sur tous les bloc de 8x8 de chacune des composantes YCbCr
		for(int i=0; i<liste.getTaille(); i++){
			for(int j=0; j<liste.getComponent(i).size(); j++){

				//Application de la DCT sur chacun des bloc 8x8 du component actuel
				F = applyDCT(liste.getComponent(i).get(j));
				liste.getComponent(i).set(j,F);

			}//Fin du parcours des 1024 bloc de 8x8
		}//Fin du parcours des trois components

	}

	/**
	 * Méthode d'application de la DCT sur les bloc 8x8
	 * 
	 * @param f le bloc 8x8 reçu en paramètre
	 * @return la matrice de nouvelles valeurs
	 */
	private double[][] applyDCT(double[][] f) {

		//la matrice de nouvelles valeurs
		double[][] F = new double[N][N];
		for (int u=0;u<N;u++) {
			for (int v=0;v<N;v++) {

				double sum = 0.0;

				for (int i=0;i<N;i++) {
					for (int j=0;j<N;j++) {
						sum+=Math.cos(((2*i+1)/(2.0*N))*u*Math.PI)*Math.cos(((2*j+1)/(2.0*N))*v*Math.PI)*f[i][j];
					}
				}

				sum*=((c[u]*c[v])/4.0);
				F[u][v]=sum;

			}
		}

		return F;
	}

	/**
	 * Méthode d'application de la DCT inverse sur un bloc 8x8. 
	 * 
	 * @param F le bloc 8x8 reçu en paramètre
	 * @return f, la matrice de valeurs initiale
	 */
	public double[][] dctInverse(double[][] F) {

		double[][] f = new double[N][N];

		for (int i=0;i<N;i++) {
			for (int j=0;j<N;j++) {

				double sum = 0.0;

				for (int u=0;u<N;u++) {
					for (int v=0;v<N;v++) {
						sum+=(c[u]*c[v])/4.0*Math.cos(((2*i+1)/(2.0*N))*u*Math.PI)*Math.cos(((2*j+1)/(2.0*N))*v*Math.PI)*F[u][v];
					}
				}

				f[i][j]=Math.round(sum);

			}
		}

		return f;
	}
}